package com.wuhao.code.check.model

import com.aegis.kotlin.containsAny
import com.aegis.kotlin.toUnderlineCase
import com.aegis.mybatis.xmlless.AnnotatedElementDetailInfo
import com.aegis.mybatis.xmlless.AnnotationSupport
import com.aegis.mybatis.xmlless.ElementInfoBuilder
import com.aegis.mybatis.xmlless.annotations.JoinObject
import com.aegis.mybatis.xmlless.annotations.SelectedProperties
import com.aegis.mybatis.xmlless.constant.ClassNames.JAVA_COLLECTION
import com.aegis.mybatis.xmlless.constant.ClassNames.KOTLIN_COLLECTION
import com.aegis.mybatis.xmlless.model.TypeInfo
import com.aegis.mybatis.xmlless.model.XmllessTableFieldInfo
import com.aegis.mybatis.xmlless.model.XmllessTableInfo
import com.aegis.mybatis.xmlless.model.base.BaseClassWrapper
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.wuhao.code.check.*
import com.wuhao.code.check.model.base.BasePsiClassWrapper
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.psi.psiUtil.containingClass

/**
 *
 * @author 吴昊
 * @date 2024/1/19 11:57
 * @since v0.0.0
 * @version 1.0
 */
class KtClassWrapper(private val ktClass: KtClass) : BasePsiClassWrapper<KtClass>(ktClass) {

  companion object {
    internal fun getTableName(el: PsiElement): String? {
      val tableName = el.getStringAnnoValue("com.baomidou.mybatisplus.annotation.TableName", "value")
      if (tableName != null) {
        return tableName
      }
      AnnotationSupport.TABLE_ANNOTATIONS.forEach {
        val name = el.getStringAnnoValue(it, "name")
        if (name != null) {
          return name
        }
      }
      if (el is KtClass) {
        return el.name?.toUnderlineCase()?.lowercase()
      } else if (el is PsiClass) {
        return el.name?.toUnderlineCase()?.lowercase()
      }
      return null
    }
  }

  override fun getJavaClass(): Class<*> {
    return Any::class.java
  }

  override fun getClassName(): String {
    return ktClass.fqName?.toString() ?: "UNKNOWN"
  }

  override fun initXmllessTableInfo(): XmllessTableInfo {
    val tableName = getTableName(ktClass) ?: error("无法获取表名")
    val allFields = ktClass.getAllFields()
    val keyField = allFields.find {
      it.hasAnyAnnotation(*AnnotationSupport.ID_ANNOTATIONS)
    }
    val fieldList = resolveTableFields(classElement)
    return XmllessTableInfo(
        when (keyField) {
          is KtDeclaration -> keyField.name
          else             -> null
        },
        when (keyField) {
          is KtDeclaration -> getKeyColumn(keyField)
          else             -> null
        },
        tableName,
        getClassName(),
        fieldList,
        fieldList.associateBy { it.property },
        fieldList.find {
          it.isLogicDelete
        },
        createFields()
    )
  }


  override fun isComplex(): Boolean {
    return ktClass.isComplexType()
  }

  override fun getAllSuperClassesOrInterfaces(): List<String> {
    return ktClass.getAllSuperClassNames().toList()
  }

  override fun isArray(): Boolean {
    return ktClass.fqName?.toString() == "kotlin.Array"
  }

  override fun getAllFields(): List<AnnotatedElementDetailInfo> {
    val allFields = ktClass.getAllFields().map {
      it.toAnnotationElementWrapper()
    }
    return if (this.isComplex()) {
      allFields.map { ElementInfoBuilder.build(it) }
    } else {
      listOf()
    }
  }

  override fun getRealType(): BaseClassWrapper {
    return KtClassWrapper(ktClass)
  }

  override fun isPrimitive(): Boolean {
    return ktClass.isPrimitive()
  }

  override fun isCollection(): Boolean {
    return getAllSuperClassesOrInterfaces().containsAny(
        listOf(KOTLIN_COLLECTION, JAVA_COLLECTION)
    )
  }

  override fun isString(): Boolean {
    return ktClass.name == "String"
  }

  override fun getClassFields(): List<PsiElement> {
    return classElement.getAllFields()
  }

  override fun resolveTableFields(
      modelType: KtClass
  ): List<XmllessTableFieldInfo> {
    return modelType.getAllFields().filterIsInstance<KtDeclaration>().map {
      createFieldByProperty(it)
    }
  }


  override fun <E : PsiElement> createFieldByProperty(
      field: E
  ): XmllessTableFieldInfo {
    field as KtDeclaration
    val propertyName = field.name ?: ""
    val selectedProperties = field.getStringListAnnotationValue(
        SelectedProperties::class.java.name, "value"
    )
    val genericType = resolveGenericType(field)
    val joinObject = if (field.hasAnnotation(JoinObject::class.java)) {
      KtElementJoinObjectProvider(field, genericType, selectedProperties, propertyName)
    } else {
      null
    }
    return XmllessTableFieldInfo(
        KtTableFieldWrapper(field),
        joinObject,
        genericType
    )
  }

  private fun resolveGenericType(el: KtDeclaration): TypeInfo {
    val type = if (el is KtCallableDeclaration) {
      el.typeReference?.typeElement?.actualType
    } else {
      throw IllegalArgumentException("无法解析${el.name}的类型")
    }
    if (type !is KtUserType) {
      error("无法解析${el.containingClass()?.fqName?.toString()}.${el.name}的类型")
    }
    val superClasses = type.referenceExpression?.resolve()?.getAllSuperClassNames() ?: listOf()
    val realType = if (superClasses.containsAny(listOf(KOTLIN_COLLECTION, JAVA_COLLECTION))) {
      type.typeArguments.first().typeReference?.typeElement?.actualType?.resolveKtClass()
    } else {
      type.referenceExpression?.resolve() ?: error("无法解析${el.name}的类型")
    }
    val genericType = TypeInfo(
        Any::class.java,
        when (realType) {
          is PsiClass -> {
            PsiClassWrapper(realType)
          }

          is KtClass  -> {
            KtClassWrapper(realType)
          }

          else        -> {
            error("无法解析${el.name}的类型")
          }
        }
    )
    return genericType
  }

}
